package zju.dgm.client;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import zju.dgm.dto.MyTR;
import zju.dgm.dto.RegionMapInfo;
import zju.dgm.dto.ServerInfo;
import zju.dgm.service.distsqlservice.DistSqlUtilProvider;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;


public class Main {

    static RegionMapInfo<String, ServerInfo> regionMapInfo = new RegionMapInfo<>(null, -1);
    static final String EXIT = "exit";
    static final String reqPath = "/sqljson";
    static final String defaultIp = "127.0.0.1";
    static final int defaultPort = 8881;
    static final int RETRY = 4;


    static void welcome() {
        System.out.println("this is the minisql console\n" +
                "input one sql sentence per line\n" +
                "input exit to leave");
    }

    public static void main(String[] args) throws MalformedURLException {


        welcome();

        int version = -1;
        Scanner sc = new Scanner(System.in);

        System.out.print(">>> ");
        String sql = sc.nextLine();

        while (!EXIT.equals(sql)) {

            try {

                JSONObject param = parseSql(sql);
                param.put("version", regionMapInfo.getVersion());
                // 至此有4个参数：table,type,sql,version
                ServerInfo selected = selectServer(param.getString("table"), regionMapInfo.getMap());
                String url = getURL(selected, defaultPort);
                System.out.println("the target url:" + url);
                JSONObject rst = doPost2(url, param);

                int cnt = 0;
                // 如果空说明
                while (rst == null && cnt++ < RETRY) {
                    if (selected == null) {
                        //遍历default端口
                        url = getURL(null, defaultPort + 2 * cnt);
                        System.out.println("retry : the target url:" + url);
                        rst = doPost2(url, param);
                    } else {
                        regionMapInfo.getMap().get(param.getString("table")).remove(selected);
                        selected = selectServer(param.getString("table"), regionMapInfo.getMap());
                        url = getURL(selected, defaultPort);
                        System.out.println("retry : the target url:" + url);
                        rst = doPost2(url, param);
                        // 如果找不到server了，就从头开始遍历
                        if (selected == null) {
                            cnt = 0;
                        }
                    }
                }
                if (rst != null) {
                    if (rst.containsKey("cacheMap")) {
                        RegionMapInfo<String, ServerInfo> rstInfo = JSONObject.parseObject(
                                rst.getString("cacheMap"),
                                new MyTR<RegionMapInfo<String, ServerInfo>>() {
                                }
                        );
                        if (rstInfo != null && rstInfo.getVersion() != regionMapInfo.getVersion()) {
                            regionMapInfo = rstInfo;
                        }
                        System.out.println("分配信息：" + regionMapInfo);
                    }

                    String sqlResult = rst.getString("sqlResult");
                    //------------------------------------------------- 解析json转换输出
                    output(sqlResult);
                    //-------------------------------------------------

                } else {
                    System.out.println(">>> sql执行出错，服务器未响应");
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.print(">>> ");
            sql = sc.nextLine();
            while (sql.length() == 0) {
                System.out.print(">>> ");
                sql = sc.nextLine();

            }

        }

    }

    //读入sql语句
    static JSONObject parseSql(String sql) {
        int type = -1;
        String table = "";

        //sql语句拆解成单词存入数组
        String[] arr = sql.split(" ");
        ArrayList<String> wordList = new ArrayList<>(Arrays.asList(arr));
        //创建json实体
        JSONObject object = new JSONObject();
        System.out.println(Arrays.toString(arr));
        //根据第一个单词判断sql语句类型并执行对应操作
        switch (arr[0].toLowerCase()) {
            default:
                break;
            //create table student2(id int, name char(6) unique, score float, primary key(id));
            case "create":
                type = DistSqlUtilProvider.CREATE;
                table = arr[wordList.indexOf("table") + 1];
                if (table.contains("(")) {
                    table = table.substring(0, table.indexOf("("));
                }
                break;


            //drop table student2;
            case "drop":
                type = DistSqlUtilProvider.DELETE;
                for (int i = 0; i < arr.length; i++) {
                    if ("table".equals(arr[i])) {
                        table = arr[i + 1];
                        break;
                    }
                }
                table = table.substring(0, table.length() - 1);
                break;


            case "insert":
                type = DistSqlUtilProvider.UPDATE;
                for (int i = 0; i < arr.length; i++) {
                    if ("into".equals(arr[i])) {
                        table = arr[i + 1];
                        break;
                    }
                }
                break;

            //delete from author where a_name >'nnae';
            case "delete":
                type = DistSqlUtilProvider.UPDATE;
                for (int i = 0; i < arr.length; i++) {
                    if (arr[i].equals("from")) {
                        table = arr[i + 1];
                        break;
                    }
                }
                break;

            //select * from t1 where name='Jim';
            //select id , name from t1 where id>=6;
            case "select":
                type = DistSqlUtilProvider.SELECT;
                for (int i = 0; i < arr.length; i++) {
                    if (arr[i].equals("from")) {
                        table = arr[i + 1];
                        break;
                    }
                }
                break;

        }

        //封装成json语句，格式为{"table":"xxx","type":见DistSqlUtilProvider,"sql":"sql语句"}
        object.put("type", type);
        object.put("sql", sql);
        object.put("table", table.replaceAll(";", ""));
        System.out.println("sql初步解析结果：" + object.toJSONString());
        return object;
    }


    /**
     * 转换对应URL
     *
     * @param selected
     * @return
     */
    private static String getURL(ServerInfo selected, int defaultPort) {
        if (selected == null) {
            return "http://" + defaultIp + ":" + defaultPort + reqPath;
        } else {
            return "http://" + selected.getIp() + ":" + (selected.getPort() - 1) + reqPath;
        }
    }


    /**
     * 查询缓存表指定ServerInfo
     *
     * @return
     */
    private static ServerInfo selectServer(String table, Map<String, List<ServerInfo>> bufferMap) {

        if (bufferMap == null
                || !bufferMap.containsKey(table)
                || bufferMap.get(table) == null
                || bufferMap.get(table).size() == 0) {
            return null;
        }
        List<ServerInfo> serverList = bufferMap.get(table);
        // 随机选取一个server
        return serverList.get(new Random().nextInt(serverList.size()));
    }


    private static JSONObject doPost2(String urlString, JSONObject param) throws MalformedURLException {

        URL url = new URL(urlString);
        String result = null;
        JSONObject res = null;
        HttpClient httpClient = new HttpClient();
        PostMethod postMethod = new PostMethod(urlString);
        postMethod.addRequestHeader("Accept", "*/*");
        postMethod.addRequestHeader("Connection", "Keep-Alive");
        //设置json格式传送
        postMethod.addRequestHeader("Content-Type", "application/json");
        //必须设置下面这个Header
        postMethod.addRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36");
        postMethod.setRequestBody(param.toJSONString());
        try {
            int code = httpClient.executeMethod(postMethod);

            if (code == 200) {
                System.out.println("sql执行成功");
                res = JSONObject.parseObject(postMethod.getResponseBodyAsString());
            }
        } catch (Exception e) {
            System.out.println("错误信息：" + e.getMessage());
            return null;

        }
        return res;
    }

    static void output(String jsonMessage){

        //非查询结果直接输出
        if(!jsonMessage.substring(0,1).equals("[")){
            System.out.println(">>>" + jsonMessage);
        }
        //查询结果json格式解析
        else {
//            String jsonMessage = "[{'num':'成绩', '外语':88, '历史':65, '地理':99, 'object':'aaa'}," +
//                    "{'num':'兴趣', '外语':28, '历史':45, '地理':19, 'object':'bbb'}," +
//                    "{'num':'爱好', '外语':48, '历史':62, '地理':39, 'object':'ccc'}]";
            JSONArray myJsonArray = JSONArray.parseArray(jsonMessage);
            for(int i=0 ; i < myJsonArray.size() ;i++)
            {
                //获取每一个JsonObject对象
                JSONObject myJsonObject = myJsonArray.getJSONObject(i);

                //先输出表头
                if(i==0) {
                    Iterator iter = myJsonObject.entrySet().iterator();
                    while (iter.hasNext()) {
                        Map.Entry entry = (Map.Entry) iter.next();
                        System.out.printf("%-16s\t\t", entry.getKey().toString());
                        System.out.print("|");
                    }
                    System.out.println();
                }

                //逐条输出数据，注意对齐和最大宽度，已处理中英文对齐问题
                Iterator iter = myJsonObject.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry entry = (Map.Entry) iter.next();
                    System.out.printf("%-16s\t\t", entry.getValue().toString());
                    System.out.print("|");
                }
                System.out.println();

            }
        }
    }

}
